#include "mvc/view/aircraft_serializer.h"
#include "mvc/model/aircrafts/aircraft_rcurves.h"
#include "common/settings.h"
#include "common/log/QsLog.h"

#include <QStringList>

Aircraft_Serializer::Aircraft_Serializer()
{

}

/**
  * Writes the points belongs to the configuration generated to the .DAT files in disk.
  * path - path where the .DAT will be written
  * c - configuration whose points will be written
  */
void Aircraft_Serializer::write_points_to_datfiles(const QString &path, const Configuration *c)
throw(Serializer_exception)
{
    const Aircraft *air = dynamic_cast<const Aircraft *> (c);

    try
    {
        write_ls_datfile(path, air);
        write_fuselage_datfile(path, air);
        write_vtail_datfile(path, air);
        write_htail_datfile(path, air);
    }
    catch(const Serializer_write_exception &e)
    {
        throw e;
    }

}

/**
  * Reads the simulation file to find the points belongs to a determinated time of simulation
  * path - path where the simulation file is stored
  * time - the time of simulation to read
  * scale - the scale value to the points
  * Returns the time zone read.
  */
time_zone *Aircraft_Serializer::read_wake_zones(const QString &path, int time, double scale) throw(Serializer_exception)
{
    QString ctime("");
    if(time < 10)
        ctime.append("00");
    else if(time> 9 && time< 100)
        ctime.append("0");
    ctime.append(QString::number(time));

    QString wakeFile(path);
    wakeFile.append("/").append(DATS_PREFIX).append("WAKE.TEC");

    QFile file(wakeFile);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        QString r("Cannot find file ");
        r.append(wakeFile);
        throw Serializer_read_exception(r);
    }
    QTextStream in(&file);

    QString timeString("(");
    timeString.append(ctime).append(")");

    time_zone *tz = new time_zone();
    QString lineNumbers;

    QString line = in.readLine();
    while(!in.atEnd())
    {
        if( (line.contains(timeString)) || (lineNumbers.contains(timeString)) ){

            tz->colours.append(get_value_line("C=",line));
            tz->columns.append(get_value_line("J=",line).toInt());
            Zone zone;

            while(!in.atEnd())
            {
                lineNumbers = in.readLine();
                QStringList line_list = lineNumbers.split(" ", QString::SkipEmptyParts);
                if(line_list.size()==3)
                {
                    double x = line_list.at(0).toDouble() * scale;
                    double y = line_list.at(1).toDouble() * scale;
                    double z = line_list.at(2).toDouble() * scale;
                    zone.append(new Point3D(x, y, z));
                }
                else break;
            }
            tz->zones.append(zone);
            line = lineNumbers;
        }
        else
            line = in.readLine();
    }

    if(file.isOpen())
        file.close();
    return tz;
}

/**
 * @brief Aircraft_Serializer::read_coefficients_loadfiles
 * @param path
 * @param file
 * @param coefficients
 */
void Aircraft_Serializer::read_coefficients_loadfiles(const QString &path, const QString &file,
                                                      QVector<QString> *coefficients)
{
    QString allPath(path);
    allPath.append("/").append(file);

    QFile loadfile(allPath);
    if (!loadfile.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        QString r("Cannot find file ");
        r.append(allPath);
        throw Serializer_read_exception(r);
    }

    QTextStream in(&loadfile);

    QString line = in.readLine();
    QStringList _coefficients;
    typedef QStringList::const_iterator it;

    while(!in.atEnd())
    {
        if(line.contains("IT"))
        {
            _coefficients = line.split(" ");

            for(it _coefficient = _coefficients.begin(); _coefficient != _coefficients.end(); _coefficient++)
                if(!(*_coefficient).isEmpty() && (*_coefficient).compare("IT") != 0)
                    coefficients->push_back(*_coefficient);

            break;
        }

        line = in.readLine();
    }
}

/**
 * @brief Aircraft_Serializer::read_loadsfiles
 * @param path
 * @param coefficients
 * @param values
 */
void Aircraft_Serializer::read_loadsfiles(const QString &path, const QString &file,
                                          QMap<QString, QVector<double> > *c_values) throw(Serializer_exception)
{
//    cout << "TODO: Aircraft_Serializer::read_loadsfiles" << endl;
    QString allPath(path);
    allPath.append("/").append(file);

    QFile loadfile(allPath);
    if (!loadfile.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        QString r("Cannot find file ");
        r.append(allPath);
        throw Serializer_read_exception(r);
    }

    QTextStream in(&loadfile);

    QString line = in.readLine();
    QStringList _coefficients;
    QStringList _values;
    typedef QStringList::const_iterator it;

    QVector<QString> coefficients;
    while(!in.atEnd())
    {
        if(line.contains("IT"))
        {
            _coefficients = line.split(" ");

            for(it _coefficient = _coefficients.begin(); _coefficient != _coefficients.end(); _coefficient++)
                if(!(*_coefficient).isEmpty() && (*_coefficient).compare("IT") != 0)
                {
                    QVector<double> v;

                    coefficients.push_back(*_coefficient);

                    c_values->insert(*_coefficient, v);
                }

            break;
        }

        line = in.readLine();
    }

    while(!in.atEnd())
    {
        line = in.readLine();

        _values = line.split(" ");

        int i = 0;
        for(it _value = _values.begin(); _value != _values.end(); _value++)
            if((*_value).length() > 2)
                (*c_values)[coefficients.at(i++)].push_back((*_value).toDouble());
    }

    loadfile.close();
}

//-------------------------------------------------
//              Private Functions
//-------------------------------------------------

/**
  *
  */
QString Aircraft_Serializer::get_value_line(QString parameter,  QString line)
{
    QString value = "";
    if(parameter.contains("J")){
        value = line.remove(0,line.indexOf(parameter)+2);
        value.remove(line.indexOf(","),line.size());
    }
    if(parameter.contains("C")){
        value = line.remove(0,line.indexOf(parameter)+2);
        value = value.trimmed();
    }
    return value;
}

//
// Private functions
//

/**
  * writes the points belongs to the wings of the aircraft to LS.DAT file.
  * path - path where the LS.DAT will be stored.
  * air - configuration whose points will be written
  */
void Aircraft_Serializer::write_ls_datfile(const QString &path, const Aircraft *air)
throw (Serializer_write_exception)
{
    const AGeometry &ls_geometry = air->get_a_geometry(p_ls);

    // Write header
    QVector<QString> header = make_header(ls_geometry);
    write_info(path, d_wings, header, QIODevice::WriteOnly | QIODevice::Text);

    // write points
    write_points(path, d_wings, ls_geometry.get_points(true));
}

/**
  * writes the points belongs to the fuselage of the aircraft to FUSELAGE.DAT file.
  * path - path where the FUSELAGE.DAT will be stored.
  * air - configuration whose points will be written
  */
void Aircraft_Serializer::write_fuselage_datfile(const QString &path, const Aircraft *air)
throw (Serializer_write_exception)
{
    const AGeometry &fuselage_geometry = air->get_a_geometry(p_fuselage);

    // Write header
    QVector<QString> header = make_header(fuselage_geometry);
    write_info(path, d_fuselage, header, QIODevice::WriteOnly | QIODevice::Text);

    // Write points
    write_points(path, d_fuselage, fuselage_geometry.get_points(false));

    // Write back
    QVector<int> intersection_panels = air->get_wings_fuselage_intersection_panels();

    QVector<QString> back;
    for(int i = 0; i < intersection_panels.size(); i++)
        back.push_back(QString::number(intersection_panels.at(i)));

    write_info(path, d_fuselage, back, QIODevice::Append | QIODevice::Text);
}

/**
  * writes the points belongs to the vertical tail of the aircraft to VTAIL.DAT file.
  * path - path where the VTAIL.DAT will be stored.
  * air - configuration whose points will be written
  */
void Aircraft_Serializer::write_vtail_datfile(const QString &path, const Aircraft *air)
throw (Serializer_write_exception)
{
    const AGeometry &vtail_geometry = air->get_a_geometry(p_vtail);

    // Write header
    QVector<QString> header = make_header(vtail_geometry, true);
    write_info(path, d_vtail, header, QIODevice::WriteOnly | QIODevice::Text);

    // Write points
    write_points(path, d_vtail, vtail_geometry.get_points(true));

    // Write back
    QVector<QString> back;
    int row_s = (vtail_geometry.total_points(false) / vtail_geometry.rows());
    int col_s = vtail_geometry.rows() - 1;

    for(int col = 0; col < col_s; col++)
        for(int row = 1; row < row_s; row++)
        {
            int fst_n = row + (col * row_s);
            QString column;
            column.append(QString::number(fst_n)).append(" ");
            column.append(QString::number(fst_n + row_s)).append(" ");
            column.append(QString::number(fst_n + row_s + 1)).append(" ");
            column.append(QString::number(fst_n + 1)).append(" ");
            back.push_back(column);
        }

    write_info(path, d_vtail, back, QIODevice::Append | QIODevice::Text);
}

/**
  * writes the points belongs to the horizontal tail of the aircraft to HTAIL.DAT file.
  * path - path where the HTAIL.DAT will be stored.
  * air - configuration whose points will be written
  */
void Aircraft_Serializer::write_htail_datfile(const QString &path, const Aircraft *air)
throw (Serializer_write_exception)
{
    const AGeometry &htail_geometry = air->get_a_geometry(p_htail);

    // Write header
    QVector<QString> header = make_header(htail_geometry);
    write_info(path, d_htail, header, QIODevice::WriteOnly | QIODevice::Text);

    // Write points
    write_points(path, d_htail, htail_geometry.get_points(true));
}

/**
  * writes the points belongs to the wings of the aircraft to .DAT file.
  * path - path where the .DAT will be stored.
  * datfile - the datfile name
  * points - points will be written to the datfile
  */
void Aircraft_Serializer::write_points(const QString &path, const QString &datfile, QVector<Point3D *> points)
throw (Serializer_write_exception)
{

    QString dirfile(path);

    dirfile.append(DATS_PREFIX).append(datfile);

    QFile file(dirfile);

    if(not file.open(QIODevice::Append | QIODevice::Text))
    {
        QString r("Could not write points on ");
        r.append(datfile);
        throw Serializer_write_exception(r);
    }

    QTextStream out(&file);

    for(int i = 0; i < points.size(); ++i)
    {
        QString number;
        QString line(" ");

        line.append(number.sprintf("%.10f", points.at(i)->x)).append(" ");
        line.append(number.sprintf("%.10f", points.at(i)->y)).append(" ");
        line.append(number.sprintf("%.10f", points.at(i)->z)).append(" ");

        out << line.toStdString().c_str() << endl;
    }

    out.flush();
    file.close();
}

/**
  * writes the extra information to the .DAT file.
  * path - path where the .DAT will be stored.
  * datfile - the datfile name
  * info - the extra info to be written to the datfile
  * mode - the mode to open the datfile
  */
void Aircraft_Serializer::write_info(const QString &path, const QString &datfile,
                                     const QVector<QString> &info, QIODevice::OpenMode mode)
throw (Serializer_write_exception)
{
    QString dirfile(path);

    dirfile.append(DATS_PREFIX).append(datfile);

    QFile file(dirfile);

    if(not file.open(mode))
    {
        QString fail("Cannot find file in write_datfile_header: ");
        fail.append(dirfile);
        throw Serializer_write_exception(fail);
    }

    QTextStream out(&file);
    for (int var = 0; var < info.size(); ++var)
        out << info.at(var) << endl;

    file.close();
}

/**
  * makes the header to be written to the .DAT file.
  * geometry - the geometry to get the extra info
  * panles_info - if its true, the extra info will be about panels amounts else the extra info
  * will be about the knots amounts of the geometry.
  * returns the extra info to write.
  */
QVector<QString> Aircraft_Serializer::make_header(const AGeometry &geometry, bool panels_info) const
{
    QVector<QString> header;

    int n_componentes = geometry.total_components();
    for(int i = 0; i < n_componentes; i++)
    {
        const AGeometry *cc = geometry.get_component(i);
        int n_rows = cc->rows();
        int n_cols = cc->total_points(false) / cc->rows();

        int r_info = n_rows - 1;
        int c_info = n_cols - 1;

        if(panels_info)
        {
            r_info = (n_rows - 1) * (n_cols - 1);
            c_info = n_rows * n_cols;
        }

        QString rows_info("! NROWS");
        QString cols_info("! NCOLS");

        rows_info.append(QString::number(i + 1));
        cols_info.append(QString::number(i + 1));

        QString h_info = QString::number(r_info).append("   ").append(QString::number(c_info));
        h_info.append("    ").append(rows_info).append("    ").append(cols_info);

        header.push_back(h_info);
    }

    return header;
}
